#!bin/perl
################################################################################
#Code Convention Checker for Perl modules
#Convention: 	1. Code column length no longer than 80 characters;
#				2. No cyclic dependency with "use" directive.
################################################################################

use strict;
use Getopt::Std;
use English;
use Cwd;
use File::Spec;
use PPI;
use Data::Dumper;
use vars qw ( %opt );
use constant {
    COLUMN_WIDTH_MAX => 80,
    TABSIZE          => 4
};

use List::Util qw/ shuffle min sum /;
my $TRAINING_ATTR = +{
	str => 1,
	wiz => 1,
	def => 1,
	res => 1,
	dex => 1,
};

sub genRandSlots {
	# Before modification:
	# This method uses ad-hoc heuristics for shifting the 
	# probabilistic density in various ways.
	# regression tests MUST be run for confirmation
	# Modification of ALL magic numbers generates non-linear effects.

	my ($total, $single_max) = @_;
	my ($limit) = (values %$single_max);

	# dark magic No.1
	# decrease this and the prob curve is more concentrated(peaky).
	# also it affects the top 10% prob. modify with care.
	# increase this ( 0.001+ ) and slot full probability will
	# roughly increase 0.1% (absolute value, not relative)
	my $magic1 = 0.07;

	my $assign = $limit * $magic1;
	$assign = 1 if $assign < 1;
	my $slots = +{};
	
	my $runningTime = 1000;
	while ( $total > 0 && --$runningTime > 0 ){

		my ($key) = shuffle(keys %$single_max);
		my $val = ($slots->{$key} or 0);

		if ( $val == $single_max->{$key} ){
			next;
		}

		my $available_pts = min( $limit - $val, $total, $assign);
		my $pt = getRandInRange(1, $available_pts);

		$slots->{$key} += $pt;
		$total -= $pt;
		
		my $ratio = ($val + $pt) / $single_max->{$key};

		# dark magic No.2
		# sets the difficulty zone. 0.9 means numbers above 90% start
		# to get less frequent. Effectiveness depends on magic3.
		# This produces a smooth "restriction". It does not cut
		# off the probability abruptly. May need to push it below 0.9
		# for higher level of difficulty.
		my $magic2 = 0.85;

		if ( $ratio >= $magic2 ){

			# dark magic No.3 - may be the darkest of all
			# Decreasing magic3 raises the level of difficulty.
			my $magic3 = 0.58;

			my $thresh = sqrt($ratio) - $magic3;
			my $rand = rand();
			if ( $rand < $thresh){
				$slots->{$key} -= $pt;
				$total += $pt;
			}
		}
		if ( $ratio == 1 ){
			# play hard-to-get
			my $rand = rand();
			my $magic4 = 0.3;
			if ( $rand > $magic4 ){
				$slots->{$key} -= $pt;
				$total += $pt;
			}
		}

	}
	return $slots;
}


sub getRand {
	my ($max) = @_;
	return int( rand($max) );
}

#----- return random number [from, to] as int
sub getRandInRange {
	my ($from, $to) = @_;
	if ($from > $to) { ($from, $to) = ($to, $from); }
	my $res = getRand( $to - $from+1 ) + $from;
	return $res;
}

sub test{
		my $total = shift;
		my $single = 250;
		$total *= $single * 3;

		my $single_max = +{ 0 => $single,
							1 => $single,
							2 => $single,
							3 => $single,
							4 => $single,
							};
		my $trials = 10000;
		my $count = +{};
		for ( 1 .. $trials ){
			my $slots = genRandSlots($total, $single_max);
			die if sum(values %$slots) != $total;
			for (keys %$slots){
				die if $slots->{$_} > $single; 
			}
			$count->{$slots->{$_}}++ for keys %$slots;
		}
		for my $perc ( 0 .. 100 ){
			my $_ = int($perc * $single / 100);
			print "$perc% ".'=' x 
				( $count->{$_}  / ($trials / $single / 5)) . "\n";
		}
		my $ratio = 0;
		for (reverse 0.9 * $single .. $single ){
			$ratio += $count->{$_} / $trials * 100;
			print "$_:$ratio% ";
		}
}

sub main{

	my @case = (0.2,
			#0.4, 
			#0.6, 
			#0.8,
				1 );
	for my $case ( @case ){
		test($case);
		#sleep(1);
	}

}

&main();
#my $eof = <STDIN>; 
